//============================================================================
// Name        : WindM.cpp
// Author      : Clara Granell i Roger Pes
// Version     :
// Copyright   : Your copyright notice
// Description : WindMill Process, Ansi-style
//============================================================================

#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "specs.h"
#include <signal.h>
#include <time.h>
#include <mqueue.h>
#include <semaphore.h>
#include <errno.h>
#include <string.h>

sem_t *mutex1, mutex2, *sem_fi;

float realData=0.0;
mqd_t q1,q2,q3;
char *buf1,*buf2,*buf3;
int sizeq1,sizeq2,sizeq3;

void lecturaActiva()
{
	unsigned int prio;
	int error;
	float read;
	// Recepcio de l'angle
	error=mq_receive (q2, buf2, sizeq2, &prio);
	if(error==-1)
		{
			printf("%d Error rebent valor ",getpid());
			sem_post(sem_fi);
		}else{
			read=atof(buf2);
			// Sincronitza amb la lectura de la variable compartida
			sem_wait(&mutex2);
			realData=read;
			sem_post(&mutex2);
		}
}


static void handlerReader(sigval_t info)
{
	int error;

	// Evita que altres processos utilitzin les cues
	sem_wait(mutex1);

	int mida;
	mida=sprintf(buf1,"%d",getpid());
	// Envia el ID del moli
	error=mq_send(q1, buf1, mida, 1);
	if(error)
	{
		printf("No puc enviar el meu id a la cua %d\n",errno);
		sem_post(sem_fi);
	}else{
		lecturaActiva();
	}
	// Allibera a un altre proces que vulgui utilitzar les cues de lectura
	sem_post(mutex1);

}

static void handlerWriter(sigval_t info)
{
	float data;
	int error;


	if(realData!=0.0)
	{
		// Sincronitza amb la lectura de la variable compartida
		sem_wait(&mutex2);
		data=realData;
		sem_post(&mutex2);
		int mida=sprintf(buf3,"%d %f",getpid(),data);
		// Envia la consigna
		error=mq_send(q3, buf3, mida, 1);
		if(error)
		{
			printf("Fi de la simulacio\n");
			sem_post(sem_fi);
		}
	}
}

int installTimer(void (handler)(sigval_t info), long long offsetTime, long long frequency )
{
	#define CLOCKID CLOCK_REALTIME
	#define SIG SIGRTMIN

	timer_t timerid;
	struct sigevent sev;
	struct itimerspec its;

	// Crea el timer amb el handler especificat
	   sev.sigev_notify = SIGEV_THREAD;
	    sev.sigev_notify_function = handler;
	    sev.sigev_notify_attributes = NULL;
	    sev.sigev_value.sival_ptr = &timerid;
		if (timer_create(CLOCK_REALTIME,&sev,&timerid) == -1) {
			printf("Error en la creació del timer\n");
			return(3);
		}

	// Programa el timer amb el offset i frequencia indicats
	its.it_value.tv_sec = offsetTime / CONST_NSEG;
	its.it_value.tv_nsec = offsetTime % 1000000000;
	its.it_interval.tv_sec = frequency / CONST_NSEG;
	its.it_interval.tv_nsec = frequency % 1000000000;

	if (timer_settime(timerid, 0, &its, NULL) == -1)
		return 4;

	return 0;
}

int main( int argc, char** argv)
{
	pid_t pID;
	int fd1, fd2;


	// Inicialitza la memoria compartida entre processos
	// Semafor de finalitzacio i semafor de lectures atomiques
	fd1 = shm_open("mutex1", O_RDWR | O_CREAT, 0777);
	ftruncate( fd1, sizeof( sem_t ) );
	mutex1 = (sem_t *)mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);

	fd2 = shm_open("sem_fi", O_RDWR | O_CREAT, 0777);
	ftruncate( fd2, sizeof( sem_t ) );
	sem_fi = (sem_t *)mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);


	if(argc>1)
	{
		int processNum=atoi(argv[1]);
		printf("Simulació amb %d processos.\n", processNum);
		int i;

		// Inicialització dels semafors entre processos
		sem_init(sem_fi,1,0);
		sem_init(mutex1,1,1);

		// Comencem amb les cues destruides
		mq_unlink(CUA_LECT_VENT1);
		mq_unlink(CUA_LECT_VENT2);
		mq_unlink(CUA_ESCR_ORIENT);

		for(i=0;i<processNum;i++)
		{
			// Creacio dels molins amb fork de memoria privada
			pID = fork();
			if(pID==0){
				//Semafor d'acces a les variables compartides entre threads
				sem_init(&mutex2,0,1);


				//Inicialització de les cues amb espera activa per a la creacio
				struct mq_attr attr;

				do{q1=mq_open(CUA_LECT_VENT1,O_WRONLY,S_IRUSR|S_IWUSR,NULL);if(q1==-1)usleep(10000);}while(q1==-1);
				mq_getattr(q1,&attr);sizeq1=attr.mq_msgsize;

				do{q2=mq_open(CUA_LECT_VENT2, O_RDONLY,S_IRUSR|S_IWUSR,NULL);if(q2==-1)usleep(10000);}while(q2==-1);
				mq_getattr(q2,&attr);sizeq2=attr.mq_msgsize;

				do{q3=mq_open (CUA_ESCR_ORIENT, O_WRONLY,S_IRUSR|S_IWUSR,NULL);if(q3==-1)usleep(10000);}while(q3==-1);
				mq_getattr(q3,&attr);sizeq3=attr.mq_msgsize;

				// Inicialització dels buffers d'escriptura o lectura
				buf1=(char *)malloc(sizeof(char *)*sizeq1);
				buf2=(char *)malloc(sizeof(char *)*sizeq2);
				buf3=(char *)malloc(sizeof(char *)*sizeq3);

				// Programacio dels timers de lectura i escriptura
				installTimer(handlerReader, CONST_NSEG/FREQ_VENT, CONST_NSEG/FREQ_VENT);
				installTimer(handlerWriter, CONST_NSEG/FREQ_VENT, CONST_NSEG/FREQ_VENT);

				break;
			}
		}

		if(pID!=0){
			printf("Polsa Enter per finalitzar els processos\n");
			scanf("%s",NULL);
			sem_post(sem_fi);
		}

		// Espera a que algun proces doni per acabada l'execucio
		sem_wait(sem_fi);
		// Finlalitza el seguent proces
		sem_post(sem_fi);

		// Alliberament de recursos
		free(buf1);
		free(buf2);
		free(buf3);

		mq_close(q1);
		mq_close(q2);
		mq_close(q3);

		mq_unlink(CUA_LECT_VENT1);
		mq_unlink(CUA_LECT_VENT2);
		mq_unlink(CUA_ESCR_ORIENT);
	}

	printf("Finalitzat proces %d\n",getpid());
	return 0;
}
